package step

import (
	"bytes"
	"crypto/sha256"
	"github.com/wanchain/go-mpc/log"
	"github.com/wanchain/go-mpc/storeman/osmconf"
	schcomm "github.com/wanchain/go-mpc/storeman/schnorrcomm"
	mpcprotocol "github.com/wanchain/go-mpc/storeman/storemanmpc/protocol"
	"math/big"
)

type MpcApprovedNumStepUni struct {
	BaseStep
	isLeader    bool
	approvedNum *big.Int
}

func CreateMpcApprovedNumStepUni(totalNum int) *MpcApprovedNumStepUni {
	log.SyslogInfo("CreateMpcRRcvInterStep begin")

	mpc := &MpcApprovedNumStepUni{
		*CreateBaseStep(totalNum, -1),
		false,
		nil}
	return mpc
}

func (ptStep *MpcApprovedNumStepUni) InitStep(result mpcprotocol.MpcResultInterface) error {
	// todo
	// for leader , read approvedNum
	// for follow,  do nothing
	log.SyslogDebug("MpcApprovedNumStepUni::InitStep begin", "ctx", ptStep.GetMpcContextId())
	ptStep.BaseStep.InitStep(result)
	_, grpIdStr, err := ptStep.BaseStep.GetGrpId()
	if err != nil {
		log.SyslogErr("MpcApprovedNumStepUni", "HandleMessage error", err.Error(), "ctx", ptStep.GetMpcContextId())
	}

	selfId, _ := osmconf.GetOsmConf().GetSelfNodeId()
	index, _ := osmconf.GetOsmConf().GetInxByNodeId(grpIdStr, selfId)

	// leader
	if index == 0 {
		ret, err := result.GetValue(mpcprotocol.ApprovedMpcNumUni)
		if err != nil {
			log.SyslogErr("MpcApprovedNumStepUni", "InitStep.getValue error", err.Error(), "ctx", ptStep.GetMpcContextId())
			return err
		}
		ptStep.approvedNum = &ret[0]
		log.SyslogDebug("MpcApprovedNumStepUni::InitStep end", "ctx", ptStep.GetMpcContextId())
	}

	return nil
}

func (ptStep *MpcApprovedNumStepUni) CreateMessage() []mpcprotocol.StepMessage {
	log.SyslogDebug("MpcApprovedNumStepUni::CreateMessage begin", "ctx", ptStep.GetMpcContextId())

	_, grpIdStr, err := ptStep.BaseStep.GetGrpId()

	if err != nil {
		log.SyslogErr("MpcApprovedNumStepUni", "HandleMessage error", err.Error(), "ctx", ptStep.GetMpcContextId())
	}

	selfId, _ := osmconf.GetOsmConf().GetSelfNodeId()
	index, _ := osmconf.GetOsmConf().GetInxByNodeId(grpIdStr, selfId)

	// leader
	if index == 0 {
		message := make([]mpcprotocol.StepMessage, 1)
		message[0].MsgCode = mpcprotocol.MPCMessage
		message[0].PeerID = nil

		var buf bytes.Buffer
		buf.Write(ptStep.approvedNum.Bytes())
		h := sha256.Sum256(buf.Bytes())

		prv, err := osmconf.GetOsmConf().GetSelfPrvKey()
		if err != nil {
			return nil
		}
		r, s, _ := schcomm.SignInternalData(prv, h[:])

		message[0].Data = make([]big.Int, 3)
		message[0].Data[0] = *ptStep.approvedNum
		message[0].Data[1] = *r
		message[0].Data[2] = *s

		log.SyslogDebug("MpcApprovedNumStepUni::CreateMessage end", "ctx", ptStep.GetMpcContextId())
		return message
	}
	return nil
}

func (ptStep *MpcApprovedNumStepUni) HandleMessage(msg *mpcprotocol.StepMessage) bool {
	// todo
	// if from leader, update waitingCount
	// else do nothing.

	log.SyslogInfo("MpcApprovedNumStepUni::HandleMessage begin", "ctx", ptStep.GetMpcContextId())
	ptStep.approvedNum = &msg.Data[0]
	r := msg.Data[1]
	s := msg.Data[2]

	var buf bytes.Buffer
	buf.Write(msg.Data[0].Bytes())
	h := sha256.Sum256(buf.Bytes())

	_, grpIdStr, err := ptStep.BaseStep.GetGrpId()
	if err != nil {
		log.SyslogErr("MpcApprovedNumStepUni", "HandleMessage error", err.Error(), "ctx", ptStep.GetMpcContextId())
	}

	index, _ := osmconf.GetOsmConf().GetInxByNodeId(grpIdStr, msg.PeerID)
	if index == 0 {
		senderPk, err := osmconf.GetOsmConf().GetPKByNodeId(grpIdStr, msg.PeerID)
		if err != nil {
			log.SyslogErr("MpcApprovedNumStepUni", "GetPKByNodeId error", err.Error(), "ctx", ptStep.GetMpcContextId())
		}

		bVerifySig := schcomm.VerifyInternalData(senderPk, h[:], &r, &s)

		if bVerifySig {
			log.SyslogInfo("MpcApprovedNumStepUni::HandleMessage check sig success", "ctx", ptStep.GetMpcContextId())
			log.SyslogInfo("MpcApprovedNumStepUni", "approvedNum", ptStep.approvedNum.Int64(), "ctx", ptStep.GetMpcContextId())
			// update waiting
			ptStep.GetMpcContext().UptAllStepsWtCount(int(ptStep.approvedNum.Int64()))
		} else {
			log.SyslogErr("MpcApprovedNumStepUni::HandleMessage check sig fail", "ctx", ptStep.GetMpcContextId())
		}
		// update no work indexes.
		log.SyslogDebug("MpcApprovedNumStepUni::HandleMessage end", "ctx", ptStep.GetMpcContextId())
		return true
	} else {
		log.SyslogDebug("MpcApprovedNumStepUni::HandleMessage not from leader", "ctx", ptStep.GetMpcContextId())
		return false
	}
}

func (ptStep *MpcApprovedNumStepUni) FinishStep(result mpcprotocol.MpcResultInterface, mpc mpcprotocol.StoremanManager) error {
	log.SyslogDebug("MpcApprovedNumStepUni::FinishStep begin", "ctx", ptStep.GetMpcContextId())
	err := ptStep.BaseStep.FinishStep()
	if err != nil {
		return err
	}
	log.SyslogInfo("MpcApprovedNumStepUni::FinishStep", "ctx", ptStep.GetMpcContextId())
	return nil
}
